跳到主要内容

Go 的堆栈相关

Go 栈保存的是什么?

在Go语言中,栈(Stack)保存了函数调用的上下文信息。每当一个函数被调用时,其相关的信息会被保存在栈上,包括函数的参数、局部变量、返回地址等。

栈是一种具有后进先出(LIFO)特性的数据结构,用于管理函数调用过程中的内存分配和回收。当一个函数被调用时,其相关信息被压入栈顶,当函数执行完毕或返回时,其信息被从栈顶弹出。

Go语言的栈帧(Stack Frame)通常包含以下内容:

  • 函数的参数值
  • 函数的局部变量
  • 返回地址:指向调用该函数的指令的地址,用于在函数执行完毕后返回到正确的位置继续执行。

在栈上保存函数调用信息的好处是,栈的操作非常高效。由于栈的特性,函数调用时的入栈和出栈操作非常快速,仅需要对栈顶指针进行简单的移动即可完成。

Go 堆保存的是什么?

在Go语言中,堆(Heap)用于保存动态分配的内存对象。堆是一块用于动态分配内存的区域,用于存储程序运行时创建的对象。

在Go中,通过 new 关键字或 make 函数创建的对象,以及通过 new 分配的指针类型,都存储在堆上(还有一些是逃逸出去的指针类型)。这些对象包括各种数据结构、数组、切片、映射、通道和自定义类型等。

与栈不同,堆上的内存分配和回收是通过垃圾回收(Garbage Collection)机制来管理的。垃圾回收器会定期扫描堆上的对象,识别不再被引用的对象,并回收它们所占用的内存。这使得程序员可以专注于对象的创建和使用,而无需手动管理内存的分配和释放。

在 Go 语言中,使用 new 关键字来分配零值对象的指针,例如 new(int) 会在堆上分配一个用于存储 int 类型零值的对象,并返回指向该对象的指针。

需要注意的是,Go 语言的栈和堆在内存管理上具有不同的特点。栈上的内存分配和回收是由编译器自动处理的,而堆上的内存管理则依赖于垃圾回收器。了解这些内存管理的特点可以帮助开发人员编写更高效和可靠的代码。

Go 的栈大小是多少?

Go语言中的栈大小在不同的操作系统和编译器中有所不同,并且可以通过运行时环境进行配置。以下是一些常见的默认栈大小设置:

  • Windows(64位):默认栈大小为1MB。
  • macOS(64位):默认栈大小为8MB。
  • Linux(64位):默认栈大小为2MB。

在 Go 应用程序运行时,每个 goroutine 都维护着一个自己的栈区,这个栈区只能自己使用不能被其他 goroutine 使用。栈区的初始大小是 2KB(比 x86_64 架构下线程的默认栈 2M 要小很多),在 goroutine 运行的时候栈区会按照需要增长和收缩,占用的内存最大限制的默认值在64位系统上是 1GB。栈大小的初始值和上限这部分的设置都可以在 Go 的源码 runtime/stack.go 里找到:

// rumtime.stack.go
// The minimum size of stack used by Go code
_StackMin = 2048

var maxstacksize uintptr = 1 << 20 // enough until runtime.main sets it for real

References